//
// Created by hugan on 2020/10/16.
//

#ifndef JOYSTICKCPP_JOYSTICK_UTIL_H
#define JOYSTICKCPP_JOYSTICK_UTIL_H

#include <iostream>
#include <windows.h>
#include <strsafe.h>
#include <dinput.h>
#include <regex>

using namespace std;

class JoystickUtil {
public:
    constexpr static const char *GUID_SPLITER = "-";

    static void initEmptyState(DIJOYSTATE2 &s) {
        s.lX = 0;
        s.lY = 0;
        s.lZ = 0;
        s.lRx = 0;
        s.lRy = 0;
        s.lRz = 0;
        for (auto &v : s.rglSlider) v = 0;
        for (auto &v : s.rgdwPOV) v = 0xFFFFFFFF;
        for (auto &v : s.rgbButtons) v = 0;
        //
        s.lVX = 0;
        s.lVY = 0;
        s.lVZ = 0;
        s.lVRx = 0;
        s.lVRy = 0;
        s.lVRz = 0;
        for (auto &v : s.rglVSlider) v = 0;
        //
        s.lAX = 0;
        s.lAY = 0;
        s.lAZ = 0;
        s.lARx = 0;
        s.lARy = 0;
        s.lARz = 0;
        for (auto &v : s.rglASlider) v = 0;
        //
        s.lFX = 0;
        s.lFY = 0;
        s.lFZ = 0;
        s.lFRx = 0;
        s.lFRy = 0;
        s.lFRz = 0;
        for (auto &v : s.rglFSlider) v = 0;
    }


    static bool equalDIJoyState2(DIJOYSTATE2 *a, DIJOYSTATE2 *b) {
        if ((*a).lX != (*b).lX
            || (*a).lY != (*b).lY
            || (*a).lZ != (*b).lZ
            || (*a).lRx != (*b).lRx
            || (*a).lRy != (*b).lRy
            || (*a).lRz != (*b).lRz
            || (*a).lVX != (*b).lVX
            || (*a).lVY != (*b).lVY
            || (*a).lVZ != (*b).lVZ
            || (*a).lVRx != (*b).lVRx
            || (*a).lVRy != (*b).lVRy
            || (*a).lVRz != (*b).lVRz
            || (*a).lAX != (*b).lAX
            || (*a).lAY != (*b).lAY
            || (*a).lAZ != (*b).lAZ
            || (*a).lARx != (*b).lARx
            || (*a).lARy != (*b).lARy
            || (*a).lARz != (*b).lARz
            || (*a).lFX != (*b).lFX
            || (*a).lFY != (*b).lFY
            || (*a).lFZ != (*b).lFZ
            || (*a).lFRx != (*b).lFRx
            || (*a).lFRy != (*b).lFRy
            || (*a).lFRz != (*b).lFRz) {
            return false;
        }
        for (int i = 0; i < 2; i++) if ((*a).rglSlider[i] != (*b).rglSlider[i]) return false;
        for (int i = 0; i < 4; i++) if ((*a).rgdwPOV[i] != (*b).rgdwPOV[i]) return false;
        for (int i = 0; i < 128; i++) if ((*a).rgbButtons[i] != (*b).rgbButtons[i]) return false;
        for (int i = 0; i < 2; i++) if ((*a).rglVSlider[i] != (*b).rglVSlider[i]) return false;
        for (int i = 0; i < 2; i++) if ((*a).rglASlider[i] != (*b).rglASlider[i]) return false;
        for (int i = 0; i < 2; i++) if ((*a).rglFSlider[i] != (*b).rglFSlider[i]) return false;
        return true;
    }

    /**
     * GUID转字符串,格式:1+1+1+8位十六进制数(自动前补零),以 GUID_SPLITER 分隔;如"12345678-1234-1234-1234567812345678"
     * @param dest 长度为36
     * @note 原生API,string转GUID及反转,https://www.cnblogs.com/kernel0815/p/4254683.html;不如自己写
     */
    static void getGUIDString(const GUID &guid, char *dest) {
        /*
         * 08:不足8位时,前面补零
         * X:用大写输出十六进制数(x是小写)
         * 位数:35+1,最后一个是停止符?
         * 格式说明
         * https://docs.microsoft.com/en-us/cpp/c-runtime-library/format-specification-syntax-printf-and-wprintf-functions?view=msvc-160#flags
         */
        StringCbPrintfA(dest, 36, "%08X-%04X-%04X-%02X%02X%02X%02X%02X%02X%02X%02X",
                        guid.Data1, guid.Data2, guid.Data3,
                        guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3],
                        guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);

    }

    /**
     * 字符串转GUID
     * @param guidString 来自{getGUIDString()}生成的字符串
     */
    static void getGuid(GUID &g, const string &str) {
        //cout << "guid=" << str << endl;
        regex ws_re(GUID_SPLITER);
        vector<string> split(sregex_token_iterator(str.begin(), str.end(), ws_re, -1),
                             sregex_token_iterator());
        //cout << "size=" << split.size() << endl;
        //for (auto &&s: split) cout << s << endl;
        if (split.size() != 4) {
            std::cerr << __FILE__ << "[row:" << __LINE__ << "]:wrong format guid string:" << str << std::endl;
            return;
        }

        //strtoul会忽略非数字的内容,如:空格,'0x'
        char *pBuff;
        g.Data1 = strtoul(split[0].data(), &pBuff, 16);
        g.Data2 = strtoul(split[1].data(), &pBuff, 16);
        g.Data3 = strtoul(split[2].data(), &pBuff, 16);
        for (int i = 0; i < 8; i++) {
            string hex = split[3].substr(i * 2, 2);
//        cout << "i=" << i << ", sub=" << hex << endl;
            g.Data4[i] = strtoul(hex.data(), &pBuff, 16);
        }
    }
};

#endif //JOYSTICKCPP_JOYSTICK_UTIL_H